1987-BHV-02 S.26-31
Die Grundintelligenz der HD 64180-CoprozessorkarteArnulf SoppDer HD 64180 kann seine erweiterten Fähigkeiten dem Computer nur auf Anforderung des Z80 zur Verfügung stellen. Er muß daher per Software in die Lage versetzt werden, eine solche Anforderung zur Kenntnis zu nehmen und zu interpretieren. Auf Helmut Bernhardts HD-Board ist ein EPROM vorgesehen, das dieses Programm enthält. Sein Inhalt ist im Listing 1 wiedergegeben. Der HD liest nicht nur nach Reset, sondern auch nach einem TRAP-Interrupt an der Stelle logisch 0000h. Darauf wird hier nicht weiter eingegangen, weil Gerald Schröder darüber bereits schrieb. In diesem Falle verzweigt der HD in eine Fehlerbehandlungsroutine, die den unbekannten Befehl imitiert und dann zum alten Programm zurückkehrt. Diese Routine habe ich fast wörtlich von Geralds Programm übernommen, das in der 17. Ausgabe des Clubinfos abgedruckt ist. Deshalb sei hier nur auf den geringen Unterschied zu Geralds Routine hingewiesen: Die Interrupts müssen nicht mit DI maskiert werden, weil sich die Speicherkonfiguration während der Trap-Behandlung nicht ändert. Ein INT dürfte getrost eintreten. In diesem EPROM entfällt natürlich auch das Banking, das für das Genie 2s im Info beschrieben wurde. Ein TRAP kann im EPROM nicht auftreten, weil keine Illegals verwendet sind. Die Routine liegt daher nur zur Bearbeitung aus dem RAM bereit. Wurde 0000h nach einem Reset gelesen, finden einige Initialisierungen statt. Für den leistungsfähigen HD-Befehl OTIMR (gib ein Datum aus (HL) auf Port (C) aus, erhöhe C und HL, erniedrige B, wiederhole dies, bis B=0 ist) wird HL als Zeiger auf einige Initialisierungsdaten (Label initab) aufgesetzt. Diese Daten konfigurieren im wesentlichen die MMU (Memory Management Unit) des HD, um aus dem Adreßraum von insgesamt 512 kB folgende "Fenster" der CPU zugänglich zu machen:
In dieser Tabelle von Daten sind weitere Informationen für den HD enthalten, die aber für das Verständnis des Folgenden nicht von Bedeutung sind. Soweit sie sich nicht aus den Kommentaren des Listings erklären, möchte ich auf das Datenbuch verweisen. Die CPU hat nun insgesamt 64 kB Speicher verfügbar, deren physikalische Lage oben dargestellt ist. Nach dem Befehl OTIMR wird HL nun auf das letzte Byte einer zweiten Tabelle gestellt (tabend). Sie enthält Daten für den DMAC (Direct Memory Access Controller). Er kann unabhängig von der Konfiguration der MMU Daten aus jedem beliebigen Teil des Gesamtspeichers in jeden beliebigen anderen transferieren. Die Werte vom Label srclog an besagen in diesem Falle, daß die Daten ab dma0 bis zum Label prt in den physikalischen Speicherbereich ab 7.F000h übertragen werden sollen. Die beiden Bytes davor in der Tabelle bestimmen die Art des DMA-Zugriffs und führen schließlich zum Start des DMA. Die Routine zur Programmierung des DMA-Controllers (ab Label dma0) ist so gestaltet, daß der Programmierer nicht weiter über die einigermaßen komplizierten Port-Ausgaben bescheidwissen muß. Analog zum Befehl LDIR lädt er lediglich HL mit der Quelladresse, DE mit der Zieladresse (beide innerhalb je eines beliebigen physikalischen 64-kB-Blocks) und BC als Bytezähler. Zusätzlich muß nur noch im Akku angegeben werden, in welchem physikalischen 64-kB-Block des Gesamtspeichers von 512 kB Quelle und Ziel zu suchen sind. Die Bits 0-2 tragen diese Information für die Quelle, Bit 4-6 für das Ziel. Ich habe diese Bits bewußt ausgewählt, weil dann der Akku Quelle und Ziel sozusagen im Klartext, nämlich in je einer Hex-Ziffer enthält. Je nach dem, welche Vorarbeit bei der Programmierung des DMAC schon geleistet ist, kann auch an späterer Stelle eingesprungen werden. Der EPROM-Inhalt von srclog bis prt-1 ist nun ins obere RAM übertragen. Er enthält auch eine Interrupt-Vektortabelle für den Fall des IM 2. Der Z80 kann nämlich mit einer geringfügigen Hardware-Änderung mit der Ansprache des Ports FBh einen Interrupt am HD-Pin INT2 auslösen, wodurch der HD die Buskontrolle übernimmt. Ebenso ist es denkbar, daß der HD im Interrupt-Betrieb für den Z80 mit irgendwelcher Peripherie kommuniziert. Das EPROM ist auf diese Fälle vorbereitet. Da die Vektoren von Fall zu Fall programmiert werden müssen, sind sie im EPROM alle zu FFFFh gesetzt, so daß sie entweder umgebrannt oder später soft aufgesetzt werden können. Nur der Vektor für einen Interrupt auf den Pin INT2 des HD ist auf intz80 gesetzt, weil er anstelle des Polling die Busübergabe signalisieren könnte. Die geschähe nun freilich asynchron. Ein Job könnte gerade in Arbeit sein. Daher gibt es das Flag rdyflag, aus dem der HD ablesen kann, ob er die neue Aufgabe bereits übernehmen kann. Wenn nicht, fährt er erst mit dem alten Job fort. Wenn er später wieder bei waitz80 landet und pollt, findet er sofort RAM und tut nun verspätet, was von ihm verlangt wird. Im ausschließlichen Polling-Betrieb merkt der HD nichts von der Z80-Ansprache bevor er nach waitz80 zurückkehrt. Schließlich wird noch der gesamte Inhalt des EPROMs nach 6.0000-7.FFFF übertragen, damit der HD später auch vom Z80-RAM aus bequemen Zugriff auf Routinen hat, die im EPROM niedergelegt sind. Ohne diese Kopie ginge das nicht, weil das Z80-RAM und das EPROM niemals gleichzeitig erreichbar sind. Solche Routinen können nachträglich ins EPROM gebrannt werden. Ihre Funktion muß nichts mit dem HD zu tun haben. Da ab physikalisch 7.F000h bereits Common 1 definiert ist, sind die per DMA nach oben geladenen Programmteile sofort verfügbar. Deshalb können bereits die Wertetabelle und das DMA-Unterprogramm im oberen RAM benutzt werden, um das EPROM nach oben zu kopieren. Damit hat sich das EPROM nun selber überflüssig gemacht. Dennoch bleibt es eingeblendet. Der HD liest nun fortgesetzt an der Speicherstelle 0000h und prüft, ob dort der Opcode 00h für NOP steht. Wenn ja, handelt es sich an dieser Adresse immer noch um sein EPROM. Wenn ein beliebiger anderer Code gelesen wird, liegt dort unten nun der bisherige Z80-Speicher vor. Dies ist das Signal, daß der Z80 soeben die Kontrolle über seinen Systembus an den HD abgab. Kein Betriebssystem wird bei 0000h ausgerechnet mit einem NOP beginnen, so daß Zweifel über EPROM oder Z80-Memory nicht bestehen können. Und wenn doch, sollte der User ohnehin zuvor für den HD einen JP (Opcode C3h) zu einer Trap-Routine dort gepatcht haben. Auf Computer, die in ihrem unteren Adreßraum ein nicht schreibbares ROM haben, wird an späterer Stelle eingegangen.Die Adresse 0000h für das Polling resultiert aus der Forderung, daß das Board mit seinem EPROM möglichst in jedem Z80-Rechner ohne Änderungen benutzbar sein sollte. In den TRS-80-kompatiblen Computern mit NEWDOS o. ä. gäbe es viele Möglichkeiten, auf eine andere Adresse auszuweichen. Jedoch schon unter CP/M fangen die Restriktionen an. Ab 0000h beansprucht CP/M den Speicher für sich. Dort steht ein JP-Befehl für den Warmstart mit CTRL-C. Unter CP/M muß deshalb eine Trap-Routine prüfen, ob es sich tatsächlich um einen TRAP handelt und gegf. den überschriebenen CP/M-Sprung nachholen. Dies gilt für viele Computer analog. Nur solche, die NEWDOS/G-DOS/TRSDOS fahren und ein Boot-EPROM besitzen, können sicher sein, daß 0000h nur bei einem TRAP angesprungen wird. Die beiden Mikroprozessoren müssen einander mitteilen können, was anliegt. Eine Möglichkeit bestünde darin, daß der HD bei einer Ansprache durch den Z80 immer an dieselbe vereinbarte Speicherstelle springt und einfach tut, was da steht. Für diejenigen unter uns, die auch noch im Tiefschlaf vom Hacken träumen, wäre dies zweifellos die flexibelste Lösung. Ein bequemerer Weg, der sogar unter BASIC beschritten werden kann, wurde hier gewählt: An der Stelle 0003h für TRS-80 usw.: kommt später) wird der Stackpointer abgelegt. Alles Weitere, was zur Kommunikation der beiden Mikroprozessoren erforderlich ist, muß nun über den Stack abgewickelt werden. Der Stackpoin-ter des HD wird dazu aus 0003/4h geladen. Hier gibt es folgende Vereinbarung: Zuunterst im Stack kann AF gePOPt werden. Im Akku steht dann ein Signalbyte, das dem HD verrät, welche von fünf möglichen Anforderungen der Z80 an ihn stellt (was in welcher Reihenfolge dazu auf den Stack gelegt werden muß, ist in den Listings reichlich kommentiert und teilweise tabellarisch zusammengefaßt):
Wo dieses Programm zu suchen ist, steht in einer Tabelle. Die Zahl im Register C wird zunächst verdoppelt, weil jeder Vektor (Zeiger) eine Zwei-Byte-Adresse enthält. Nachdem das MSB auf 00h gesetzt ist, steht nun in BC der Summand zur Basisadresse einer Vektortabelle. BC plus Basis ist dann der Zeiger auf die Adresse, an der das gewünschte Programm steht. Das ist dasselbe Prinzip, das bei Interrupts des Modus IM 2 angewendet wird. Ein solches Programm ist in seiner Flexibilität etwa der endgültigen Busübergabe vergleichbar, kehrt aber immer nach waitz80 zurück. Listing 7 zeigt ein Anwendungsbeispiel. Mit Listing 4 soll gezeigt werden, wie die Trap-Routine des EPROMs vom Z80-RAM aus aufgerufen werden kann. Das Programm wurde mit dem Genie 3s erstellt, dürfte aber wohl auf allen TRS-80-kompatiblen Computern laufen, die kein ROM an den unteren Adressen haben. Andernfalls muß nach einem anderen Speicherabschnitt für die Routine trap Ausschau gehalten werden. Er darf auf keinen Fall die logische Adresse F000h überschreiten. Die Routine liest die MMU-Register BBR und CBAR aus und puffert ihren Inhalt, damit er später wieder restauriert werden kann. Dann wird Common 1 wie bei Reset definiert (ab 7.F000h), damit die Trap-Routine unter der logischen Adresse F100h gefunden wird. Da (z. B. unter BASIC) der Stack im Himem liegen könnte, darf bei der Bankerei nichts gePUSHt werden; alles wird über Speicherladebefehle abgewickelt. Dieses Programm stellt die Voraussetzung, daß nur im User-RAM ein TRAP auftreten wird. Das DOS, das auch in seiner eigenen Bank werkelt, benutzt keine Illegals, so daß von dort nichts zu befürchten ist. Der Anwender muß sich eben damit abfinden, daß er bei der Arbeit in einer anderen Bank nur die dokumentierten Befehle verwenden darf. Die ziemlich lendenlahme TRAP-Prozedur legt ohnehin nahe, Illegals durch andere Befehle zu ersetzen. Es fällt auch auf, daß das TRAP-Bit nicht gelesen wird. Das Programm setzt einfach voraus, daß ein RST 00h nur bei Trap auftritt, denn bei Reset wird ja im Boot-EPROM (neuerdings auch der Coprozessorkarte) gelesen. Auch bei ROM-Computern mit nachträglich eingebautem Banker ist das so. Ein Reset-Taster existiert dort nicht, und nach dem Einschalten ist sowieso das ROM selektiert. Der angebliche RESET dieser Maschinen ist in Wirklichkeit ein NMI und springt bei 0066h ein. Dort steht nach wie vor der alte Code. Aber unter CP/M wäre wegen des RST 00h bei Warmstart mit CTRL-C die Prüfung auf TRAP unumgänglich. CP/M stellt ebenfalls die Bedingung, unbedingt die Interrupts zu maskieren, weil es im Himem arbeitet, wo nach dem Umschalten nun eine andere Bank vorliegt. Die oben beschriebene Akrobatik zum Ermitteln des EI/DI-Zustandes braucht dabei aber nicht stattzufinden. Es geht auch so, wie im Listing 5 beschrieben. Der intern belegte Port 34h enthält nämlich nicht nur die Bits UFO und TRAP (Bits 6 und 7), sondern auch ITEO-2 (Bits 0, 1, 2). Wenn diese Bits 0 lauten, werden Interrupts an den korrespondierenden INT-Eingängen der CPU ignoriert. Der Input von 34h kann also an den Ausgang geladen, dann manipuliert und chließlich am Ausgang wieder restauriert werden. Der HD 64180 bietet die Möglichkeit, eine programmierbare Anzahl von WAIT-Zyklen für Speicher- und I/O-Operationen getrennt zu generieren. Man möchte natürlich seine Maschine so schnell wie möglich arbeiten lassen, also so wenige WAITs einfügen, daß das System noch eben zuverlässig arbeitet. Im Listing 6 wird gezeigt, wie man diese Grenze herausfinden kann. Ein Job-Programm verändert in der EPROM-Tabelle initab das Byte für den Port 32h. Es bestimmt die Anzahl der WAITs, und zwar für den Speicher in den Bits 6-7 und für Portansprachen in den Bits 4-5. Nach der Bearbeitung des Jobs wird die Buskontrolle endgültig an den HD 64180 übergeben. Wenn das System mit diesem Programm abstürzt oder eine I/O-Operation nicht das erwartete Ergebnis bringt, war der Akku zu sparsam beladen. Durch wiederholten Aufruf dieses Programms mit variiertem Wert für den Akku wird experimentell bestimmt, wie schnell höchstens mit der eigenen Hardware gearbeitet werden kann. Der so gefundene Wert sollte dann immer bei der ersten Ansprache des HD nach einem Reset gern. dem Muster in Listing 6 an den Anfang der Tabelle initab gesetzt werden. Mit korrekt geladenem Akku kann dies auch als Programm zur endgültigen Busübergabe genommen werden. Abgesehen vom Signalbyte für den HD und den Adressen, die im Stack übergeben werden, braucht dem Stack dabei keine weitere Aufmerksamkeit gewidmet zu werden, weil der Stackpointer beim Sprung nach DOS-Ready neu geladen wird. Alle sieben Listings enthalten bereits die Mnemonics einiger zusätzlicher Befehle, die der HD 64180 kennt. Da ZEUS sie jedoch noch nicht kennt, stehen sie wie Kommentare hinter einem Semikolon. Um ZEUS begreiflich zu machen, was auf die Diskette assembliert werden soll, sind die Hex-Entsprechungen dieser Befehle darunter immer als DBs programmiert. Kein Programm wird jemals fertig. Der Inhalt dieses EPROMs wird sich noch ändern, besonders in den ersten Monaten der Erprobung in der Praxis. Das Listing 1 ist deshalb vielleicht (oder eher wahrscheinlich) zum Zeitpunkt seiner Veröffentlichung bereits überholt. Wer das EPROM für seine eigene Coprozessorkarte haben möchte, erhält die jeweils neueste Version. Ein aktualisiertes Listing wird nicht mitgeliefert. Sein Inhalt kann mit einem Job-Programm in den unteren Speicher kopiert und dort leicht disassembliert werden. Von den möglichen 64 kB des EPROMs ist mit bisher gerade einem Kilobyte nur ein Bruchteil für die Grundintelligenz der Coprozessorkarte verbraucht. Viele nützliche Routinen, z. B. ein Spooler mit enormem Pufferplatz oder eine intelligente RAM-Floppy, finden hier noch Platz. Zu diesem Zweck sind bestimmte Adressen mit FFh-Bytes freigehalten worden. FFh kann bei einem bereits gebrannten EPROM nachgebrannt werden. So sind beispielsweise noch sämtliche Restart-Adressen frei (außer RST 00h, versteht sich). Auch Füllbytes, die nachfolgende Routinen oder Tabellen auf "gerade" Adressen schieben sollen, lauten FFh und können noch sinnvoll programmiert werden, ebenso die Tabelle der Vektor-Utilities. Dasselbe gilt für den NMI-Einsprung an 0066h, obwohl die Karte nicht für einen NMI im EPROM (sehr wohl im RAM) vorgesehen ist. Aber irgendein Hard-Freak unter euch könnte sich das nachrüsten wollen. 00001 ; Listing 1: EPROM-Inhalt der Coprozessorkarte 00002 00003 ***************************************************** 00004 Boot-EPROM für die Coprozessorkarte 00005 mit dem HD 64180 00006 00007 für das Genie 3s und die meisten anderen Z80-Rechner 00008 00009 von Helmut Bernhardt 00010 00011 (C) 1987 Arnulf Sopp 00012 00013 die Trap-Routine entspricht weitestgehend der Vorlage 00014 von Gerald Schröder (C) 1987 00015 ;****************************************************** 00016 00017 ;Puffer für den Stackpointer des Z80 bei Übergabe der Buskontr. an den HD 00018 ;die Adresse gilt für Computer mit einem Boot-EPROM und RAM ab 0000 0003 00019 spbuff EQU 0003h ;für ROM-Computer: auf freie RAM-Adresse ändern 00020 0000 00021 ORG 0000h ;physikalisch 0.0000 00022 00023 ;RST 00: nach Reset die MMU des HD 64180 initialisieren usw. 0000 00 00024 NOP ;wegen des Pollings an logisch 0000 0001 ED5E 00025 IM 2 ;wegen mögl. Ansprache des Z80 über INT2 0003 214501 00026 LD HL,initab+2 ;Tabelle der internen Port-Initialwerte 0006 1833 00027 JR gorst00 ;dort weiter 00028 00029 ;weitere RST-Routinen können nachgerüstet werden 0008 FFFF 00030 DW Offffh,Offffh,Offffh,Offffh ;Platzhalter für RST 08 0010 FFFF 00031 DW Offffh,Offffh,Offffh,Offffh ;dto. RST 10 0018 FFFF 00032 DW Offffh,Offffh,Offffh,Offffh ;dto. RST 18 0020 FFFF 00033 DW Offffh,Offffh,Offffh,Offffh ;dto. RST 20 0028 FFFF 00034 DW Offffh,Offffh,Offffh,Offffh ;dto. RST 28 0030 FFFF 00035 DW Offffh,Offffh,Offffh,Offffh ;dto. RST 30 0038 FF 00036 DB Offh,Offh,Offh ;dto. RST 38 (JP genügt) 00037 00038 ;Fortsetzung der RST 00-Routine (Reset oder Trag) 0038 01340C 00039 gorst00 LD BC,0c34h ;12 Werte ab Port 34h 00040 ; OTIMR ;Initialwerte für interne 003E ED 00041 DB 0edh,93h ;Ports aufsetzen (s. o.) 00042 00043 ;Warteprogramm und EPROM-Inhalt per DMA ins Himem transferieren 0040 219800 00044 LD HL,tabend ;Ende der Wertetabelle für den DMAC 0043 CD8700 00045 CALL dma2 ;per DMA Himem-Programm hochtransferieren 0046 68 00046 LD L,B ;HL (- 0000 (Anfang EPROM, H=00, BC=0000) 0047 50 00047 LD D,B ;DE (- 0000 (LSW logische Zieladresse) 0048 58 00048 LD E,8 0049 3E60 00049 LD A,60h ;Quellber. 0.xxxx, Zielber. 6.xxxx (MSB) 0048 CD6900 00050 CALL dma0 ;EPROM-Inhalt nach 6.0000-6.FFFF übertr. 004E C33CF0 00051 JP inidone+offs ;Warteroutine anspringen 00052 00053 ;Platzhalter bis zum NMI-Einsprung an 0066 0051 FFFF 00054 DW Offffh,Offffh,Offffh,Offffh,Offffh,Offffh,Offffh,Offffh 0061 FF 00055 DB Offh,Offh,Offh,Offh,Offh 00056 00057 ;Einsprung für NMI (nicht vorgesehen, kann aber nachgerüstet werden) 0066 FF 00058 nmi DB Offh,Offh,Offh ;3 Bytes, können mit JP überschr. werden 00059 F000 00060 himem EQU 0f000h :Common 1, Arbeitsadresse des Programms EF97 00061 offs EQU himem-$ ;Offset zur Arbeitsadresse des Programms 00062 00063 ;der folgende Teil des EPROM-Inhalts arbeitet im Speicher ab 7.F000 00064 00065 ;Unterprogr. für DMA: HL = LSW Quelle, DE = LSW Ziel, BC = Bytezähler, 00066 ;A Bit 0-2 = MSB der Quelle, A Bit 4-6 = MSB des Ziels 0069 222BF0 00067 dma0 LD (srclog+offs),HL ;Quellbereich LSW x.HL 006C ED532EF0 00068 LD (dstlog+offs),DE ;Zielbereich LSW x.DE 0070 ED4331F0 00069 LD (dmacnt+offs),BC ;Bytezähler für DMA aufsetzen 0074 F5 00070 PUSH AF ;phys. Quell- und Ziel-MSB retten 0075 E60F 00071 AND 0fh ;Quellbits maskieren 0077 322DF0 00072 LD (srcphys+offs),A ;Quellbereich phys. A.HL 007A F1 00073 POP AF ;MSB des Quell- und Zielblocks 0078 OF 00074 RRCA ;in die unteren Bits rotieren 007C OF 00075 RRCA 007D OF 00076 RRCA 007E OF 00077 RRCA 007F E60F 00078 AND Ofh ;Zielbits maskieren 0081 3230F0 00079 LD (dstphys+offs),A ;Zielbereich phys. A.DE 00080 ;Einsprung in DMA, falls die Tabelle der Adressen vorbereitet ist 0084 0084 2132F0 00081 dma1 LD HL,tabend+offs ;Beginn der DMA-Wertetabelle 00082 ;Bank-unabhängiger Teil des Programms, kann von überallher gecallt werden 0087 012708 00083 dma2 LD BC,0827h ;8 Werte ab Port 27 abwärts 00084 ; OTDMR ;DMAC aufsetzen 008A ED 00085 DB 0edh,9bh ;(s. o.) 008C 013102 00086 LD BC,0231h ;2 Werte ab Port 31 abwärts 00087 ; OTDMR ;Rest aufsetzen u. DMA abschießen 008F ED 00088 DB 0edh,9bh ;(s. o.) 0091 C9 00089 RET 00090 00091 ;Init.-Werte des DMAC zur Kopie des Himem-Programms ins RAM ab 7.F000 0092 0092 41 00092 DB 41h ;30. DSTAT: Kanal 0, Zustand 'scharf' 0093 02 00093 DB 02h ;31, DMODE: Speicher zu Speicher, Burst-Modus 0094 6900 00094 srclog DW dma0 :20/1, SAROL/H: Quelladresse LSW dma0 0096 00 00095 srcphys DB 00h ;22, SAROB: MSB 0.xxxx 0097 00F0 00096 dstlog DW himem ;23/4, DAROL/H: Zieladresse LSW F000 0099 07 00097 dstphys DB 07h ;25, DAROB: MSB 7.xxxx 009A 0403 00098 dmacnt DW prt-dma0 ;26/7, BCROL/H: Bytezähler für die Programmlänge 0098 00099 tabend EQU S-1 ;Ende der Tab.. womit der Zeiger HL geladen wird 00100 00101 ;nach der Initialis. auf Ansprache des Z80 warten, sie interpretieren 009C 00 00102 rdyflag DB 00h ;Flag für noch nicht beendeten Job 009D 21DAF0 00103 waitz80 LD HL,initab+offs ;Tabelle der internen Port-Initialwerte 00A0 01320E 00104 LD BC,0e32h ;14 Werte ab Port 32h 00105 ; OTIMR ;Initialwerte für interne Ports aufsetzen 00A3 ED 00106 DB 0edh,93h ;(s. o.) 00A5 3EF3 00107 inidone LD A,inttab+offs/256 ;MSB der Interrupt-Vektortabelle 00A7 ED47 00108 LD I,A ;ins Interrupt-Vektorregister 00A9 D3FB 00109 copdone OUT (0fbh),A ;falls der HD noch die Buskontrolle hat 00AB 3100F1 00110 LD SP,trap+offs ;Stack im freien Bereich 00AE 210000 00111 LD HL,0000h ;Prüfadresse (im Z80-RAM unkritisch) 0081 AF 00112 XOR A ;A (- 00, Flag für beendeten Job 0082 3233F0 00113 LD (rdyflag+offs),A ;Bereitsch. des HD dort vermerken 0085 FB 00114 EI ;für evtl. Z80-Interrupt 0086 7E 00115 polloop LD A,(HL) ;Testbyte laden 0067 B7 00116 OR A ;immer noch 00? (EPROM noch eingeblendet) 0088 28FC 00117 JR Z,polloop ;falls noch EPROM 00118 00119 ;der Z80 gab soeben die Kontrolle an den HD ab (auch Entry für Z80-INT) 008A F3 00120 intz80 DI ;wegen unklaren Interrupt-Modus usw. 0088 2A33F0 00121 LD HL,(rdyflag+offs) ;Flag für anhängigen Job 008E 7E 00122 LD A,(HL) ;Flag laden 00BF 67 00123 OR A ;ist z. Zt. noch ein Job in Arbeit? 0000 2802 00124 JR Z,nojob ;falls nicht mehr 0002 ED4D 00125 RETI ;falls ja (nur bei INT vom Z80) 00C4 ED780300 00126 nojob LD SP,(spbuff) ;Stackpointer des Z80 übernehmen 00127 00128 ;vereinbarte Vorgaben für den Stack des Z80: 00129 ; (SP+0): evtl. Wegweiser in die Utility-Vektortabelle 00130 ; (SP+1): Signalbyte für die Art der übergebenen Aufgabe 00131 ; 00132 ;bei endgültiger Übernahme: 00133 ; (SP+2), (SP+3): Platz für den Befehlsstring zum Banking 00134 ; (SP+4), (SP+5): Startadresse (Entrypoint) für den HD 64180 00135 : evtl. darüber : Registerinhalte, die gePOPt werden können 00136 ; 00137 ;bei vorübergehendem Job: 00138 ; (SP+2), (SP+3): Startadresse (Entrypoint) für den HD 64180 00139 ; 00140 ;bei Übergabe von Rechenergebnissen: 00141 ; (SP+2), (SP+3): Pufferadresse im Z80-RAM 00142 ; 00143 ;bei Speicherkopie von 0.0000-0.FFFF nach 1.0000-3.FFFF im Z80-RAM 00144 ;und bei Abruf einer programmierten Utility aus der Vektortabelle: 00145 ; keine weiteren Daten 00146 ; 00147 ;Vereinbarung für das Signalbyte (SP+0) (Register B nach POP BC) 00148 ; B:00: endgültige Übernahme des Systembusses durch den HD 64180 00149 ; B=01: 2 kB Ergebnisse eines früheren Jobs aus 7.F800 übergeben 00150 ; B=02: eine Utility aus der Vektortabelle aufrufen 00151 ; B=FF: 0.0000-0.FFFF in die 64-kB-Blocks bis 3.FFFF kopieren 00152 ; FF>8>02: einen sonstigen vorübergehenden Job vom Z80 übernehmen 00153 0008 C1 00154 POP BC ;unterste Stack-Ebene lesen 00C9 78 00155 LD A,B ;Task-Signalbyte vom Z80-Stack 00CA 77 00156 LD (HL),A ;Flag für anhängenden Job setzen 00CB 3C 00157 INC A ;A=FF, das RAM 0.0000-0.FFFF kopieren? 00CC 2840 00158 JR Z,copy ;falls ja 00CE 3D 00159 DEC A ;A=00, Kontrolle endgültig übernehmen? 00CF 200F 00160 JR NZ,job ;>00: Job übernehmen o. Ergebn. übergeben 00161 00162 ;der Z80 hat im Stack signalisiert, daß er die Kontrolle ganz abgibt 00D1 2173F0 00163 LD HL,switch+offs ;Befehlsfolge zum Umschalten von Common 1 00D4 D1 00164 POP DE ;Zieladr. des Umschaltbefehls a. d. Stack 00D5 D5 00165 PUSH DE ;noch einmal auf den Stack als RET-Adr. 0006 010400 00166 LD BC,0004h ;Länge der beiden Befehle 00D9 EDBO 00167 LDIR ;übertragen 0008 C9 00168 RET ;banken und Arbeit des Z80 übernehmen 00169 00170 switch 00171 ; OUT0 (38h),A ;A=00, Common 1 ab 0.0000 einstellen 00DC ED 00172 DB 0edh,39h,38h ;Hex-Entsprechung des Befehls 00DF C9 00173 RET ;an die vom Z80 vorgegebene Adr. springen 00174 00175 ;der Z80 hat im Stack signalisiert, daß er für den HD 64180 einen Job hat 00176 ;er muß mit RET aufhören (wird wie ein Unterprogramm behandelt) 00E0 E1 00177 job POP HL ;Einsprungsadresse für den Job 00E1 3100F1 00178 LD SP,trap+offs ;Stack wieder im eigenen RAM 00E4 E5 00179 PUSH HL ;Entry des Jobs auf den eigenen Stack 00E5 2134F0 00180 LD HL,waitz80+offs ;Rücksprungadresse 00E8 E3 00181 EX (SP),HL ;als RET-Adresse a. d. Steck, HL <- Entry 00E9 E5 00182 PUSH HL ;Programm-Entry retten 00EA 3D 00183 DEC A :A=01, Ergebnisse hinunterladen? 00E8 2816 00184 JR Z,putres1 ;falls ja 00ED 3D 00185 DEC A ;A=02, eine Utility aus d. Vektortabelle? 00EE 2823 00186 JR Z,vectask ;falls ja 00F0 210000 00187 LD HL,0000h ;Quelladresse für DMA x.0000 (LSW) 00F3 54 00188 LD D,H ;Zieladresse dto. 00F4 5D 00189 LD E,L 00F5 0100F0 00190 LD BC,himem ;Größe des zu übertrag. Speicherblocks 00F8 3E70 00191 LD A,70h ;Quelle phys. 0.xxxx, Ziel 7.xxxx (MSB) 00192 ; OUT0 (39h),A ;Bank-Area ebenfalls ab 7.0000 00FA ED 00193 DB 0edh,39h,39h ;(s. 0.) 00FD CD00F0 00194 dmaresl CALL dma0+offs ;Speicher hoch- oder runterkopieren 0100 D3FB 00195 vectret OUT (0fbh),A ;dem Z80 die Buskontrolle zurückgeben 0102 C9 00196 RET ;evtl. Programm anspr., dann nach waitz80 00197 00198 ;der Z80 hat signalisiert, daß er 2 kB Resultate aus 7.F800 abholen will 0103 D1 00199 putresl POP DE ;für Ergebnisse: DE = Zielbereich 0104 2100F8 00200 LD HL,0f800h ;Puffer für Job-Ergebnisse (Quellbereich) 0107 010008 00201 LD BC,0800h ;Länge des Puffers immer 2 kB 010A 3E07 00202 LD A,07h ;Quellbereich 7.xxxx, Zielbereich 0.xxxx 010C 18EF 00203 JR dmaresl ;Ergebnisse übertragen, zurück zu waitz80 00204 00205 ;der Z80 hat signalisiert, 0.0000-0.FFFF nach 1.0000-3.FFFF zu kopieren 010E CDBAF0 00206 COPY CALL clone+offs ;den Speicher kopieren 0111 1896 00207 JR copdone ;zurück in die Warteschleife 00208 00209 ;der Z80 ruft einen Task auf, dessen Adresse in der Vektortabelle steht 0113 2197F0 00210 vectask LD HL,vectret+offs ;Rückkehradresse für diesen Fall 0116 E3 00211 EX (SP),HL ;auf den Stack, Nonsense-Adresse löschen 0117 47 00212 LD B,A ;8 als MSB des Vektorsummanden =00 0118 CB01 00213 RLC C ;LSB verdoppeln wegen 2 Bytes pro Vektor 011A 2100F2 00214 LD HL,vectab+offs ;Beginn der Vektortabelle 011D 09 00215 ADD HL,BC ;Zeiger auf die Task-Adresse stellen 011E 7E 00216 LD A,(HL) ;das LSB der Adresse laden 011E 23 00217 INC HL ;auf das MSB stellen 0120 66 00218 LD H,(HL) ;dieses laden 0121 6F 00219 LD L,A ;HL - Adresse der Utility 0122 E9 00220 JP (HL) ;sie anspringen 00221 00222 ;UP zur Speicherkopie, das auch unabhängig von A=FF gecallt werden kann 00223 ;je nach gewünschter Blockgröße, Startbank, Zahl der Banks, Adressen usw. 00224 ;Einsprung mit unterschiedl. Registerinhalten an verschiedenen Stellen 0123 010000 00225 clone LD BC,0000h ;Bytezähler 0000 : 1.0000 (64 kB) 0126 60 00226 LD H,B ;Quelladresse LSW 0000 0127 69 00227 LD L,C 0128 50 00228 LD D,8 ;Zieladresse LSW 0000 0129 59 00229 LD E,C 012A 3E10 00230 LD A,10h ;Quelle phys. 0.0000, Ziel phys. 1.0000 0120 ED43CDF0 00231 LD (cloncnt+offs),BC ;den Zähler aufsetzen 0130 0603 00232 LD B,03h ;3 Banks zu je 64 kB 0132 C5 00233 coploop PUSH BC ;Schleifenzähler retten 0133 E5 00234 PUSH HL ;dto. LSW Quelladresse 0134 F5 00235 PUSH AF ;dto. MSB Quell- und Zieladresse 0135 010000 00236 LD BC,0000h ;Bytezähler 0000 (:1.0000) 0136 00237 cloncnt EQU $-2 ;kann auf andere Größe geändert werden 0138 CD00F0 00238 CALL dma0+offs ;eine Bank kopieren 0138 F1 00239 POP AF ;MSB Quell- und Zieladresse 013C C610 00240 ADD A,10h ;der nächste phys. 64-kB-Block 013E E1 00241 POP HL ;LSW Quelladresse 013F C1 00242 POP BC ;Schleifenzähler 0140 10F0 00243 DJNZ coploop ;bis 3 64-kB-Blocks kopiert sind 0142 C9 00244 RET 00245 00246 ;Initialwerte der internen Ports 32-3F (nach RESET erst ab 34 gelesen) 0143 F0 00247 initab DB 0f0h ;32, DCNTL: 3 WAITs für Memory und I/O 0144 00 00248 DB 00h ;33, IL: interne INTs ab 00 in der Tabelle 0145 07 00249 DB 07h ;34, ITC: alle ext. Interrupts zugelassen 0146 FF 00250 DB 0ffh ;35: belanglos 0147 83 00251 DB 83h ;36, RCR: Refreshes möglichst zeitsparend 0148 FF 00252 DB 0ffh ;37: belanglos 0149 70 00253 DB 70h ;38, CBR: Common 1 ab 7.x000 014A 00 00254 DB 00h ;39, BBR: Bank-Area ab 0.x000 014B F0 00255 DB 0f0h ;3A, CBAR: Common 0 nicht definiert 00256 ;Bits 0-3: Bank-Area: 0.0000 - 0.EFFF 00257 ;Bits 4-7: Common 1 : 7.F000 - 7.FFFF 014C FF 00258 DB 0ffh ;3B: belanglos 014D FF 00259 DB 0ffh ;3C: 014E FF 00260 DB 0ffh ;3D: 014F FF 00261 DB 0ffh ;3E: 0150 20 00262 DB 20h ;3F, ICR: interne Ports ab 00, IOSTOP-Modus 00263 00264 ;Füllbytes, um trap0 im Himem auf die "gerade' Adresse F100 zu bekommen 00265 ;hier liegt auch der HD-eigene Stack (max. 12 Ebenen!) 0151 FFFF 00266 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0161 FFFF 00267 DW 0ffffh,0ffffh,0ffffh,0ffffh 00268 00269 ;Fehlerbehandlung bzw. Illegal-Simulation nach einem TRAP-Interrupt 0169 F5 00270 trap PUSH AF ;Akku und Flags retten 00271 ; IN0 A,(34h) ;TRAP-Bit und UF0-Bit einlesen 016A ED 00272 DB 0edh,38h,34h ;(s. o.) 016D E67F 00273 AND 7fh ;TRAP-Bit zurücksetzen 00274 ; OUT0 (34h),A ;TRAP löschen 016F ED 00275 DB 0edh,39h,34h ;(s. o.) 0172 3211F1 00276 LD (ufo+offs),A ;UFO retten 0175 F1 00277 POP AF ;Akku vom Stack holen 0176 E3 00278 EX (SP),HL ;HL mit Trapadresse tauschen 0177 F5 00279 PUSH AF ;Akku wieder retten 0178 C5 00280 PUSH BC ;retten 0179 3E00 00281 LD A,00h ;UFO (Dummy-Operand) 017A 00282 ufo EQU $-1 ;je nach dem 0178 CB77 00283 BIT 6,A ;UF0-Bit gesetzt? 017D 2801 00284 JR Z,nosub ;nein, Opcode direkt davor 017F 28 00285 DEC HL ;sonst Zeiger -1 0180 2B 00286 nosub DEC HL ;Zeiger auf illegalen 0pcode 0181 7E 00287 LD A,(HL) ;Opcode laden 0182 47 00288 LD B,A ;und retten 0183 FECB 00289 CP 0cbh ;CBxx? (SLIA bzw. SLL) 0185 23 00290 INC HL ;Zeiger weiterstellen 0186 7E 00291 LD A,(HL) ;2. Byte laden 0187 200D 00292 JR NZ,ddfd ;falls 0perand mit IX/IY 0189 D620 00293 SUB 20h ;Akku korrigieren 0186 3228F1 00294 LD (cbxx+offs),A ;in den Programmtext laden 018E C1 00295 POP BC ;Register restaurieren 018F F1 00296 POP AF 0190 23 00297 INC HL ;PC neu setzen 0191 E3 00298 EX (SP),HL ;als RET-Adresse auf den Stack 0192 37 00299 SCF ;Cy - 1, Bit 0 bei SLIA/SLL immer 11 0193 CB10 00300 RL B ;rotieren (8 ist Dummy-Operand) 0194 00301 cbxx EOU S-1 ;0perand für Register B-A 0195 C9 00302 RET 00303 0196 FECB 00304 ddfd CP 0cbh ;ein ähnlicher Befehl für IX/IY? 0198 2827 00305 JR Z,ddfdcb ;falls ja 019A 3252F1 00306 LD (substit+offs),A ;nächsten 0pc. in d. Programmtext 019D FE26 00307 CP 26h ;LD HX/HY,const ? 019F 2807 00308 JR Z,ldcon ;falls ja 01A1 FEZE 00309 CP 2eh ;LD LX/LY,const ? 01A3 2803 00310 JR Z,ldcon ;falls ja 01A5 AF 00311 XOR A ;keine Konstante: NOP 01A6 1802 00312 JR gotrap ;dort weiter 01A8 23 00313 ldcon INC HL :Zeiger auf die Konstante 01A9 7E 00314 LD A,(HL) ;diese laden 01AA 3253F1 00315 gotrap LD (const+offs),A ;evtl. Konstante in den Programmtext 01AD 78 00316 LD A,B ;DD/FD zurück 01AE 324FF1 00317 LD (ddfd1+offs),A ;in den Programmtext setzen 0181 3255F1 00318 LD (ddfd2+offs),A ;dort auch 0164 C1 00319 POP BC ;Register restaurieren 0185 F1 00320 POP AF 0186 23 00321 INC HL ;PC neu setzen 0167 E3 00322 EX (SP),HL ;als RET-Adresse auf den Stack 0188 DDE5 00323 ddfd1 PUSH IX ;bzw. IY 018A E3 00324 EX (SP),HL ;HL - IX/IY 0168 7F 00325 substit LD A,A ;Operation ausführen (hier Dummy) 016C 00 00326 const DB 00h ;evtl. Konstante 01BD E3 00327 EX (SP),HL ;neues IX/IY zurück 018E DDE1 00328 ddfd2 POP IX ;bzw. IY 01C0 C9 00329 RET 00330 01C1 78 00331 ddfdcb LD A,B ;DD/FD zurück 01C2 32A2F1 00332 LD (ddfd3+offs),A ;in den Programmtext laden 0105 32A7F1 00333 LD (ddfd4+offs),A ;dort auch 01C8 23 00334 INC HL ;Zeiger auf den Offset 01C9 7E 00335 LD A,(HL) ;.d" aus (IX/IY+d) laden 01CA 32A4F1 00336 LD (dspl1+offs),A ;in den Programmtext laden 01CD 32A9F1 00337 LD (dspl2+offs),A ;dort auch 0100 23 00338 INC HL ;Zeiger auf Operation 0101 7E 00339 LD A,(HL) ;Befehl laden 0102 E607 00340 AND 07h ;Bits 0-2 maskieren 01D4 3C 00341 INC A ;+1 0105 47 00342 LD B,A ;als Zähler 0106 3E3E 00343 LD A,3eh ;0pcode 46-7E erzeugen 01D8 C608 00344 opclop1 ADD A,08h ;(46/4E/56/5E/66/6E/7E 010A 10FC 00345 DJNZ opclop1 ;= LD B/C/D/E/H/L/A,(IX+d) ) 010C 32A8F1 00346 LD (load+offs),A ;in den Programmtext 01DF 7E 00347 LD A,(HL) ;Befehl noch einmal laden 01E0 E6F8 00348 AND 0f8h ;Bits 0-2 löschen 01E2 0F 00349 RRCA ;und herausrotieren 01E3 0F 00350 RRCA 01E4 0F 00351 RRCA 01E5 3C 00352 INC A ;+1 01E6 47 00353 LD B,A ;als Zähler 01E7 3EFE 00354 LD A,0feh ;(06/0E/16/1E/26/2E/3E 01E9 C608 00355 opclop2 ADD A,08h ;= RLC/RRC/RL/RR/SLA/SRA/SRL/RES/SET ...) 01EB 48 00356 LD C,B ;NOP erzeugen (für reta) 01EC FE36 00357 CP 36h ;SLIA/SLL ? 01EE 200B 00358 JR NZ,noslia ;falls nein 01F0 0637 00359 LD B,37h ;SCF erzeugen 01F2 7E 00360 LD A,(HL) ;Befehl restaurieren 01F3 FE36 00361 CP 36h ;SLIA/SLL (IX/IY+d) ? 01F5 2002 00362 JR NZ,nopro ;falls nein, kein Problem 01F7 0EC9 00363 LD C,0c9h ;Opcode RET für reta 01F9 3E16 00364 nopro LD A,16h ;RL erzeugen (statt SLIA/SLL) 01F8 32A5F1 00365 noslia LD (opera+offs),A ;in den Programmtext 01FE 78 00366 LD A,B ;SCF oder NOP 01FF 32A1F1 00367 LD (cflag+offs),A 0202 79 00368 LD A,C ;RET oder N0P 0203 32A6F1 00369 LD (reta+offs),A 0206 C1 00370 POP BC ;Register restaurieren 0207 F1 00371 POP AF 0208 23 00372 INC HL ;PC korrigieren 0209 E3 00373 EX (SP),HL ;als RET-Adresse auf den Stack 00374 020A 00 00375 cflag NOP ;oder SCF 0208 00 00376 ddfd3 NOP ;DD oder FD für IX/IY 020C C8 00377 DB 0cbh ;immer C8 020D 00 00378 dsp11 NOP ;Displacement d bei (IX/IY+d) 020E 00 00379 opera NOP ;Operationsanweisung 020F 00 00380 reta NOP ;RET bei SLIA/SLL 0210 00 00381 ddfd4 NOP ;DD/FD für IX/IY 0211 7F 00382 load LD A,A ;Ladebefehl (hier Dummy) 0212 00 00383 dsp12 NOP ;s. dspll 0213 C9 00384 RET ;Simulation des Befehls beendet 00385 00386 ;Füllbytes für die "gerade" Adresse F200 der Utility-Vektortabelle 00387 ;mit freundlicher Empfehlung: 0214 28 00388 DM '(R) #0000 - (C) 1987 by The HACKT0RY ' 0239 48 00389 DM 'Helmut Bernhardt, Arnulf Sopp, Gerald Schröder' 0267 FFFF 00390 DW 0ffffh 00391 00392 ;bei F200 die Tabelle der Einsprungsadressen bei vektorisiertem Z80-Task 00393 ;zunächst alle auf FF (können nachgebrannt oder im RAM programm. werden) 0269 FFFF 00394 vectab DW 0ffffh,Offffh,Offffh,Offffh,Offffh,Offffh,Offffh,Offffh 0279 FFFF 00395 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0289 FFFF 00396 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0299 FFFF 00397 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 02A9 FFFF 00398 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0289 FFFF 00399 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 02C9 FFFF 00400 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0209 FFFF 00401 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 02E9 FFFF 00402 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 02F9 FFFF 00403 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0309 FFFF 00404 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0319 FFFF 00405 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0329 FFFF 00406 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0339 FFFF 00407 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0349 FFFF 00408 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 0359 FFFF 00409 DW 0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh 00410 00411 ;dahinter die Vektortabelle für Interrupts des Typs 2 00412 :im EPR0M ist nur der Einsprung für einen INT durch den Z80 initialisiert 0369 FFFF 00413 inttab DW 0ffffh ;für INTs auf den INT1-Pin (nicht init.) 0368 51F0 00414 DW intz80+offs ;dto. INT2-Pin (INT durch den Z80) 00415 ;weiter für die internen Interrupts (nicht initialisiert) 00416 ;(nur bis hierher muß das EPROM gebrannt werden, solange keine weiteren 00417 ;Interrupt-Vektoren aufgesetzt werden; es folgen vorerst nur FF-Bytes) 036D FFFF 00418 prt DW 0ffffh,0ffffh ;dto. PRT Kanal 0 und 1 0371 FFFF 00419 DW 0ffffh,0ffffh ;dto. DMA Kanal 0 und 1 0375 FFFF 00420 DW 0ffffh ;dto. CSI/0 0377 FFFF 00421 DW 0ffffh,0ffffh ;dto. ASCI Kanal 0 und 1 00422 ;dasselbe für die übrigen externen Interrupts (nicht weiter gelistet) 0378 00423 end EQU S ;Hilfslabel für Programmende und -lange 00424 00425 ;Hilfslabels zur Kontrolle v. Unterprogramm- u. Tabellenadressen im Himem F000 00426 dmaabs EQU dma0+offs ;Arbeitsadresse des Unterprogramms dma0 F028 00427 dmatabs EQU srclog+offs ;dto. Adressentabelle des DMAC F0BA 00428 clonabs EQU clone+offs ;dto. Unterprogramm clone0 F00A 00429 initabs EQU initab+offs ;dto. Tabelle der int. Port-Initialwerte F100 00430 trapabs EQU trap+offs ;dto. Trap-Routine F200 00431 vectabs EQU vectab+offs ;dto. Utility-Vektortabelle F300 00432 inttabs EQU inttab+offs ;dto. INT-Vektortabelle 00433 0000 00434 END 00000 Fehler cbxx 0194 cflag 020A clonabs F0BA cloncnt 0136 clone 0123 const 01BC copdone 00A9 coploop 0132 copy 010E ddfd 0196 ddfd1 0188 ddfd2 016E ddfd3 0206 ddfd4 0210 ddfdcb 01C1 dma0 0069 dmal 0084 dma2 0087 dmaabs F000 dmacnt 009A dmaresl 00FD dmatabs F026 dspl1 020D dsp12 0212 dstlog 0097 dstphys 0099 end 0378 gorst00 0038 gotrap 01AA himem F000 inidone 00A5 initab 0143 initabs F0DA inttab 0369 inttabs F300 intz80 00BA job 00E0 ldcon 01A8 load 0211 nmi 0066 nojob 00C4 nopro 01F9 noslia 01FB nosub 0180 offs EF97 opclop1 0108 opclop2 01E9 opera 020E polloop 0066 prt 036D putresl 0103 rdyflag 009C reta 020F spbuff 0003 srclog 0094 srcphys 0096 substit 0168 switch 000C tabend 0098 trag 0169 trapabs F100 ufo 017A vectab 0269 vectabs F200 vectask 0113 vectret 0100 waitz80 009D 00001 ; Listing 2: Der Z80 gibt dem HD 64180 einen vorübergehenden Job 00002 0003 00003 spbuff EOU 0003h ;bei TRS-80 u. ä.: stattdessen RAM-Adresse wählen 00004 5200 00005 ORG 5200h 00006 5200 211E52 00007 start LD HL,job ;Zeiger auf das Programm für den HD 5203 E5 00008 PUSH HL ;auf dem Steck ablegen 5204 7C 00009 LD A,H ;A - irgendetwas zwischen 02 und FE 5205 F5 00010 PUSH AF ;Signalbyte für 'bitte Job übernehmen!' 5206 ED730300 00011 LD (spbuff),SP ;dem HD den Stackpointer mitteilen 520A D3F6 00012 OUT (0fbh),A ;die Buskontrolle an den HD übergeben 00013 00014 ;jetzt ist der Z80 für einige Mikrosekunden im Schlummerzustand 00015 ;der HD holt sich soeben den Job nach oben 00016 ;danach: 00017 520C F1 00018 POP AF ;Stack wieder bereinigen 520D F1 00019 POP AF ;(es waren zwei Ebenen) 00020 00021 ; CALL WICHTIG ;inzwischen etwas Wesentlicheres tun 00022 520E 210038 00023 LD HL,3800h ;Bildschiradresse (sichtbar ab 3C00) 5211 E5 00024 PUSH HL ;als Pufferadresse für den HD 5212 3E01 00025 LD A,01h ;Signalbyte 'bitte Ergebnisse abliefern!' 5214 F5 00026 PUSH AF :für den HD auf den Stack 5215 ED730300 00027 LD (spbuff),SP ;wie oben 5219 D3FB 00028 OUT (0fbh),A ;Buskontrolle wieder an den HD übergeben 00029 00030 ;wieder ist für einen Moment Funkstille, während der der HD die Codefolge 00031 ;20h, 20h. 20h, ... (Leerzeichen) in den Bildschirmspeicher lädt 00032 :der Bildschirm ist jetzt gelöscht 00033 ;danach: 00034 5216 F1 00035 POP AF ;dasselbe wie oben 521C F1 00036 POP AF 521D C9 00037 RET ;oder was auch immer 00038 521E 2100F3 00039 job LD HL,0f300h ;Datenpuffer für Rechenergebnisse 5221 1101F3 00040 LD DE,0f301h ;die nächste Stelle 5224 01FF07 00041 LD BC,07ffh ;Länge 2 k8 5227 3620 00042 LD (HL),' ' ;am Pufferanfang ein Leerzeichen ablegen 5229 EDB0 00043 LDIR ;den ganzen Puffer mit Blanks füllen 5226 C9 00044 RET ;in die Warteschleife zurückkehren 00045 5200 00046 END start 00000 Fehler job 521E spbuff 0003 start 5200 00001 ; Listing 3: Der Z80 gibt die Buskontrolle endgültig an den HD ab 00002 0003 00003 spbuff EOU 0003h ;TRS-80 u. ä.: stattdessen RAM-Adresse wählen! 00004 4200 00005 ORG 4200h ;im Sektorpuffer ist Platz genug 00006 4200 211042 00007 start LD HL,entry ;Ansprungsadresse für den HD 64180 4203 E5 00008 PUSH HL ;für den HD auf den Stack 4204 211A42 00009 LD HL,bank ;Platz für das Banking-Programm des HD 4207 E5 00010 PUSH HL ;dto. 4208 AF 00011 XOR A ;A - 00, Signal für endgültige Übernahme 4209 F5 00012 PUSH AF ;dto. 420A ED730300 00013 LD (spbuff),SP ;dem HD verraten, wo der Stack ist 420E D3FB 00014 OUT (0fbh),A ;und den Bus endgültig übergeben 00015 4210 ED56 00016 entry IM 1 ;für die Interrupt-Bearbeitung von G-D0S 4212 3E01 00017 LD A,01h ;nur noch Interrupts am Pin INT0 zulassen 00018 ; OUT0 (34h),A ;Ausgabe auf den INT/TRAP-Port 4214 ED 00019 DB 0edh,39h,34h ;Hex-Entsprechung des Befehls 4217 C32D40 00020 JP 402dh ;DOS-Entry, dort steht z. B. auch EI 00021 00022 bank ;Platz für die Banking-Routine des HD 00023 4200 00024 END start 00000 Fehler bank 421A entry 4210 spbuff 0003 start 4200 00001 ; Listing 4: Einsprung in d. Trap-Routine beim Genie 3s unter G-D0S 00002 0000 00003 ORG 0000h ;RST-00-Routine 0000 C30039 00004 JP trap ;dort weitermachen 00005 3900 00006 ORG 3900h ;hier ist z. B. freies RAM 3900 322739 00007 trap LD (accbuf1),A ;Akku retten (Stack unklar, kein PUSH!) 3903 ED5F 00008 LD A,R ;Interrupt-Enable-Flipflop ins P/V-Flag 3905 F3 00009 DI ;Interrupts maskieren 3906 3EF3 00010 LD A,0f3h ;auf Verdacht: DI 3908 E20D39 00011 JP P0,intok ;falls die Interrupts disabled waren 3908 3EFB 00012 LD A,0fbh ;sonst den Befehl EI laden 390D 324439 00013 intok LD (eidi),A ;und vor den RET-Befehl patchen 00014 ; IN0 A,(38h) ;den Inhalt des CBR lesen 3910 ED 00015 DB 0edh,38h,38h ;(Hex-Entsprechung des Befehls) 3913 323939 00016 LD (cbrbuff),A ;und p,uffern 3916 3E70 00017 LD A,70h ;Common 1 des HD ab 7.x000 00018 ; OUT0 (38h),a ;auf CBR ausgeben 3918 ED 00019 DB 0edh,39h,38h ;(s. o.) 00020 ; IN0 A,(3ah) ;den Inhalt des CBAR lesen 391B ED 00021 DB 0edh,38h,3ah ;(s. o.) 391E 323E39 00022 LD (cbarbuf),A ;und p,uffern 3921 F6F0 00023 OR 0f0h ;Common 1 des HD ab 7.F000 00024 ; OUT0 (3ah),A ;MMU neu konfigurieren 3923 ED 00025 DB 0edh,39h,3ah ;(s. o.) 3926 3E00 00026 LD A,00 ;Akku restaurieren (00 ist Dummy) 3927 00027 accbufl EQU $-1 ;dort ist der Akku-Inhalt gepuffert 3928 ED733339 00028 LD (spbuff),SP ;Stackpointer retten 392C 3100F1 00029 LD SP,0f100h ;Stack im Common-1-Bereich 392F CDD0F0 00030 CALL 0f100h ;an 7F100 steht die Trap-Routine 3932 310000 00031 LD SP,0000h ;den alten Stack restaur. (Dummy-Adresse) 3933 00032 spbuff EQU $-2 ;je nach dem 3935 324339 00033 LD (accbuf2),A ;Akku und Flags retten (ohne Stack, s. o) 3938 3E00 00034 LD A,00h ;alter Inhalt des CBR (00 ist Dummy) 3939 00035 cbrbuff EQU $-1 ;je nach dem 00036 ; OUT0 (38h),A ;den alten Registerinhalt restaurieren 393A ED 00037 DB 0edh,39h,38h ;(s. o.) 393D 3E00 00038 LD A,00h ;dasselbe für das CBAR 393E 00039 cbarbuf EQU 1-1 00040 ; OUT0 (3ah),A 393F ED 00041 DB 0edh,39h,3ah 3942 3E00 00042 LD A,00h ;Akku und Flags restaurieren (s. o.) 3943 00043 accbuf2 EQU $-1 3944 FB 00044 eidi EI ;oder DI, je nach altem Zustand 3945 C9 00045 RET ;das war's 00046 0000 00047 END 00000 Fehler accbufl 3927 accbuf2 3943 cbarbuf 393E cbrbuff 3939 eidi 3944 intok 390D spbuff 3933 trap 3900 00001 ; Listing 5: möglicher TRAP-Einsprung unter CP/M u. a. 00002 0000 00003 ORG 0000h 0000 C30300 00004 JP trap 00005 00006 ;wo trap im RAM liegen kann, muß je nach CP/M-Version entschieden werden 00007 ; ORG irgendwo 00008 00009 trap 00010 ; IN0 A,(34h) ;INT/TRAP-Register lesen 0003 ED 00011 DB 0edh,38h,34h ;(Hex-Entsprechung des Befehls) 0006 CB7F 00012 BIT 7,A ;war das ein TRAP-Interrupt? 00013 ; JP NZ,warmstart ;falls nein (Sprungadresse je nach dem) 0008 E67F 00014 AND 7fh ;TRAP-Bit löschen 000A 321300 00015 LD (portbuf),A ;den Input von Port 34h puffern 000D AF 00016 XOR A ;A <- 00, auch die ITE-Bits =0 (wie DI) 00017 ; OUT0 (34h),A ;TRAP resetten und Interrupts disablen 000E ED 00018 DB 0edh,39h,34h ;(s. o.) 00019 00020 ;hier folgt die Trap-Routine (oder CALL nach 7.F100), und so endet sie: 00021 0011 F5 00022 exitrap PUSH AF ;Akku und Flags retten 0012 3E00 00023 LD A,00h ;alter Inhalt von Port 34 (Dummy-Operand) 0013 00024 portbuf EQU $-1 ;Operanden-Byte des Befehls = Port-Inhalt 00025 ; OUT0 (34h),A ;Inhalt des Ports restaurieren 0014 ED 00026 DB 0edh,39h,34h ;(s. o.) 0017 F1 00027 POP AF ;Akku und Flags restaurieren 0018 C9 00028 RET 00029 0000 00030 END 00000 Fehler exitrap 0011 portbuf 0013 trap 0003 00001 ; Listing 6: Testprogramm für maximale Arbeitsgeschwindigkeit 00002 0003 00003 spbuff EQU 0003h ;TRS-80 u. ä.: auf freie RAM-Adresse ändern! 00004 4200 00005 ORG 4200h ;das paßt in den Sektorpuffer 00006 4200 211042 00007 start LD HL,entry :Ansprungadresse für den HD 64180 4203 E5 00008 PUSH HL ;für den HD auf den Stack 4204 211F42 00009 LD HL,bank ;Platz für das Banking-Programm des HD 4207 E5 00010 PUSH HL ;dto. 4208 AF 00011 XOR A ;A - 00. Signal für endgültige Übernahme 4209 F5 00012 PUSH AF ;dto. 420A ED730300 00013 LD (spbuff),SP ;dem HD verraten, wo der Stack jetzt ist 420E D3FB 00014 OUT (Ofbh),A ;und den Bus endgültig übergeben 00015 00016 ;Einsprung bei der endgültigen Busübernahme durch den HD 64180 4210 ED56 00017 entry IM 1 ;für die Interrupt-Bearbeitung von G-DOS 4212 3E01 00018 LD A,01h ;nur noch Interrupts am Pin INT0 zulassen 00019 ; 0UT0 (34h),A ;Ausgabe auf den INT/TRAP-Port 4214 ED 00020 DB 0edh,39h,34h ;(Hex-Entsprechung des Befehls) 4217 3E00 00021 LD A,00h ;oder anderer Wert zwischen Ox und Fx 00022 ; OUT0 (32h),A ;Zahl der WAIT-Zyklen auf DCNTL ausgeben 4219 ED 00023 DB 0edh,39h,32h ;(s. o.) 421C C32040 00024 JP 402dh ;DOS-Entry, dort steht z. B. auch EI 00025 00026 bank ;Platz für die Banking-Routine des HD 00027 4200 00028 END start 00000 Fehler bank 421F entry 4210 spbuff 0003 start 4200 00001 ; Listing 7: Der vektororientierte Ansprung eines Programms 00002 00003 ;Voraussetzung: Zuvor wurden von einem Job-Programm einige Routinen ir 00004 ;gendwo ab 4.0000 abgelegt, um den Z80-Speicher davon zu entlasten. 00005 ;Sie können auch im EPR0M fertig vorliegen, müssen dann aber im RAM mit 00006 ;OUT (0FBh),A beginnen. 00007 ;Ihre Entrypoints, an denen z. B. zuerst das evtl. notwendige Banking 00008 ;stattfinden könnte, sind in der Tabelle vectab (7.F200) der Reihe nach 00009 ;abgelegt worden. 00010 ;Hier soll nun das Programm Nr. 4 etwas berechnen. Später holt das Progr. 00011 ;Nr. 2 die Ergebnisse in den Z80-Speicher, die nicht in den für Resultate 00012 ;reservierten Raum ab 7.F800 gepaßt hätten. 00013 0003 00014 spbuff EQU 0003h ;TRS-80 u. ä.: auf freie RAM-Adresse ändern! 00015 5200 00016 ORG 5200h 5200 210402 00017 start LD HL,0204h ;Signalbyte 02, Programm Nr. 4 (ab Nr. 0) 5203 E5 00018 PUSH HL ;auf den Stack für den HD 64180 5206 ED730300 00019 LD (spbuff),SP ;Stackpointer für den HD ablegen 5208 D3FB 00020 OUT (0fbh),A ;die Buskontrolle abgeben 00021 00022 ;Pause, bis der HD im Prg. Nr. 4 die Kontrolle über den Bus zurückgibt 00023 00024 ; CALL WICHTIG ;dann z. B. Bedeutendes vollbringen ... 00025 520A D1 00026 POP DE ;altes Signalbyte (Registerpaar egal) 520B 1E02 00027 LD E,02h ;Programm Nr. 2 (MSB ist noch richtig) 520D E5 00028 PUSH HL ;Anforderung für den HD auf den Stack 520E D3FB 00029 OUT (0fbh),A ;wie oben (korrekter SP noch in 7FFE) 00030 00031 ;wieder ein paar Millisekunden Pause ..., dann geht es irgendwie weiter. 00032 5200 00033 END start 00000 Fehler spbuff 0003 start 5200 |